package org.examples;

import java.util.concurrent.atomic.AtomicLong;

import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;

import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.nio.AsyncEntityProducer;
import org.apache.hc.core5.http.nio.DataStreamChannel;
import org.apache.hc.core5.util.Args;

/**
 *
 */
@Slf4j
public class TimeBasicAsyncEntityProducer implements AsyncEntityProducer {
  AtomicLong startMs;

  private final ByteBuffer bytebuf;

  private final int length;

  private final ContentType contentType;

  private final boolean chunked;

  private final AtomicReference<Exception> exception;

  public TimeBasicAsyncEntityProducer(final byte[] content, final ContentType contentType, final boolean chunked) {
    Args.notNull(content, "Content");
    this.bytebuf = ByteBuffer.wrap(content);
    this.length = this.bytebuf.remaining();
    this.contentType = contentType;
    this.chunked = chunked;
    this.exception = new AtomicReference<>();
  }

  public TimeBasicAsyncEntityProducer(final byte[] content, final ContentType contentType) {
    this(content, contentType, false);
  }

  public TimeBasicAsyncEntityProducer(final byte[] content, AtomicLong startMs) {
    this(content, ContentType.APPLICATION_OCTET_STREAM);
    this.startMs=startMs;
  }

  public TimeBasicAsyncEntityProducer(final CharSequence content, final ContentType contentType, final boolean chunked) {
    Args.notNull(content, "Content");
    this.contentType = contentType;
    final Charset charset = ContentType.getCharset(contentType, StandardCharsets.US_ASCII);
    this.bytebuf = charset.encode(CharBuffer.wrap(content));
    this.length = this.bytebuf.remaining();
    this.chunked = chunked;
    this.exception = new AtomicReference<>();
  }

  public TimeBasicAsyncEntityProducer(final CharSequence content, final ContentType contentType) {
    this(content, contentType, false);
  }

  public TimeBasicAsyncEntityProducer(final CharSequence content) {
    this(content, ContentType.TEXT_PLAIN);
  }

  @Override
  public boolean isRepeatable() {
    return true;
  }

  @Override
  public final String getContentType() {
    return contentType != null ? contentType.toString() : null;
  }

  @Override
  public long getContentLength() {
    return length;
  }

  @Override
  public int available() {
    return Integer.MAX_VALUE;
  }

  @Override
  public String getContentEncoding() {
    return null;
  }

  @Override
  public boolean isChunked() {
    return chunked;
  }

  @Override
  public Set<String> getTrailerNames() {
    return null;
  }

  @Override
  public final void produce(final DataStreamChannel channel) throws IOException {
    if (bytebuf.hasRemaining()) {
      channel.write(bytebuf);
    }
    if (!bytebuf.hasRemaining()) {
      channel.endStream();
      log.info("TimeTimeBasicAsyncEntityProducer.releaseResources()");
      startMs.set(System.currentTimeMillis());
    }

  }

  @Override
  public final void failed(final Exception cause) {
    if (exception.compareAndSet(null, cause)) {
      releaseResources();
    }
  }

  public final Exception getException() {
    return exception.get();
  }

  @Override
  public void releaseResources() {
    bytebuf.clear();
    bytebuf.limit(length);
  }

}
